home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / pc / files / t_unix / j109lxa4.tar / smtpserv.c < prev    next >
C/C++ Source or Header  |  1994-06-04  |  30KB  |  1,233 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  *  enhanced 4/88 Dave Trulli nn2z
  3.  *
  4.  * Mods by G1EMM and PA0GRI
  5.  */
  6. #include <stdio.h>
  7. #include <time.h>
  8. #ifdef UNIX
  9. #include <sys/types.h>
  10. #include <fcntl.h>
  11. #endif
  12. #if    defined(__STDC__) || defined(__TURBOC__)
  13. #include <stdarg.h>
  14. #endif
  15. #include <ctype.h>
  16. #include <setjmp.h>
  17. #include "global.h"
  18. #include "config.h"
  19. #include "mbuf.h"
  20. #include "cmdparse.h"
  21. #include "socket.h"
  22. #ifdef    LZW
  23. #include "lzw.h"
  24. #endif
  25. #include "iface.h"
  26. #include "proc.h"
  27. #include "smtp.h"
  28. #include "commands.h"
  29. #include "dirutil.h"
  30. #include "mailbox.h"
  31. #include "mailutil.h"
  32. #include "bm.h"
  33. #include "domain.h"
  34. #include "session.h"
  35. #include "files.h"
  36. #ifdef  NNTPS
  37. #include "nntp.h"
  38. #endif
  39.  
  40. char *Days[7] = {  "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  41. char *Months[12] = { "Jan","Feb","Mar","Apr","May","Jun",
  42.         "Jul","Aug","Sep","Oct","Nov","Dec" };
  43.  
  44.  
  45.  
  46. static struct list *expandalias __ARGS((struct list **head,char *user));
  47. static int  getmsgtxt __ARGS((struct smtpsv *mp));
  48. static struct smtpsv *mail_create __ARGS((void));
  49. static void mail_clean __ARGS((struct smtpsv *mp));
  50. static int mailit __ARGS((FILE *data,char *from,struct list *tolist));
  51. static int router_queue __ARGS((FILE *data,char *from,struct list *to));
  52. static void smtplog __ARGS((char *fmt,...));
  53. static void smtpserv __ARGS((int s,void *unused,void *p));
  54. static int mailuser __ARGS((FILE *data,char *from,char *to));
  55. extern int msgidcheck __ARGS((char *string));
  56.  
  57. /* Command table */
  58. static char *commands[] = {
  59.     "helo",
  60. #define    HELO_CMD    0
  61.     "noop",
  62. #define    NOOP_CMD    1
  63.     "mail from:",
  64. #define    MAIL_CMD    2
  65.     "quit",
  66. #define    QUIT_CMD    3
  67.     "rcpt to:",
  68. #define    RCPT_CMD    4
  69.     "help",
  70. #define    HELP_CMD    5
  71.     "data",
  72. #define    DATA_CMD    6
  73.     "rset",
  74. #define    RSET_CMD    7
  75.     "expn",
  76. #define EXPN_CMD    8
  77. #ifdef    LZW
  78.     "xlzw",
  79. #define XLZW_CMD    9
  80. #endif
  81.     NULLCHAR
  82. };
  83.  
  84. /* Reply messages */
  85. static char Banner[] = "220 %s SMTP ready\n";
  86. static char Closing[] = "221 Closing\n";
  87. static char Ok[] = "250 Ok\n";
  88. static char Reset[] = "250 Reset state\n";
  89. static char Sent[] = "250 Sent\n";
  90. static char Ourname[] = "250 %s, Share and Enjoy!\n";
  91. #ifdef    LZW
  92. static char LZWOk[] = "250 %d %d LZW Ok\n";
  93. static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN XLZW\n214 End\n";
  94. #else
  95. static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n";
  96. #endif
  97. static char Enter[] = "354 Enter mail, end with .\n";
  98. /* static char Lowmem[] = "421 System overloaded, try again later\n"; */
  99. static char DupBidMsg[] = "550 Duplicate BID in Message-Id line\n";
  100. static char Ioerr[] = "452 Temp file write error\n";
  101. static char Badcmd[] = "500 Command unrecognized\n";
  102. static char Syntax[] = "501 Syntax error\n";
  103. static char Needrcpt[] = "503 Need RCPT (recipient)\n";
  104. static char Unknown[] = "550 <%s> address unknown\n";
  105. static char Noalias[] = "550 No alias for <%s>\n";
  106.  
  107. static int Ssmtp = -1; /* prototype socket for service */
  108.  
  109. #ifdef    MSDOS
  110. /* Valid characters in a DOS filename matrix */
  111. static unsigned char doschars[] = {
  112.     0x00, 0x00, 0x00, 0x00, 0xfa, 0x23, 0xff, 0x03,
  113.     0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0x6f,
  114.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  115.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  116.     };
  117. static int dosfnchr __ARGS((int ch));
  118. #endif
  119.  
  120. /* Start up SMTP receiver service */
  121. int
  122. smtp1(argc,argv,p)
  123. int argc;
  124. char *argv[];
  125. void *p;
  126. {
  127.     struct sockaddr_in lsocket;
  128.     int s;
  129.  
  130.     if(Ssmtp != -1){
  131.         return 0;
  132.     }
  133.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  134.     chname(Curproc,"SMTP listener");
  135.  
  136.     lsocket.sin_family = AF_INET;
  137.     lsocket.sin_addr.s_addr = INADDR_ANY;
  138.     if(argc < 2)
  139.         lsocket.sin_port = IPPORT_SMTP;
  140.     else
  141.         lsocket.sin_port = atoi(argv[1]);
  142.  
  143.     Ssmtp = socket(AF_INET,SOCK_STREAM,0);
  144.     bind(Ssmtp,(char *)&lsocket,sizeof(lsocket));
  145.     listen(Ssmtp,1);
  146.     for(;;){
  147.         if((s = accept(Ssmtp,NULLCHAR,(int *)NULL)) == -1)
  148.             break;    /* Service is shutting down */
  149.  
  150.         /* Low mem check now done in tcpin.c - WG7J */
  151.         /* Spawn a server */
  152.         if(newproc("SMTP server",2048,smtpserv,s,NULL,NULL,0) == NULLPROC)
  153.             close_s(s);
  154.     }
  155.     return 0;
  156. }
  157.  
  158. /* Shutdown SMTP service (existing connections are allowed to finish) */
  159. int
  160. smtp0(argc,argv,p)
  161. int argc;
  162. char *argv[];
  163. void *p;
  164. {
  165.     close_s(Ssmtp);
  166.     Ssmtp = -1;
  167.     return 0;
  168. }
  169.  
  170. static void
  171. smtpserv(s,unused,p)
  172. int s;
  173. void *unused;
  174. void *p;
  175. {
  176.     struct smtpsv *mp;
  177.     char **cmdp,buf[LINELEN],*arg,*cp,*cmd,*newaddr;
  178.     struct list *ap,*list;
  179.     int cnt;
  180.     char address_type;
  181. #ifdef    LZW
  182.     extern int Smtpslzw;
  183.     int lzwbits, lzwmode;
  184. #endif
  185.  
  186.     sockmode(s,SOCK_ASCII);
  187.     sockowner(s,Curproc);        /* We own it now */
  188.     log(s,"open SMTP");
  189.  
  190.     if((mp = mail_create()) == NULLSMTPSV){
  191.         tputs(Nospace);
  192.         log(s,"close SMTP - no space");
  193.         close_s(s);
  194.         return;
  195.     }
  196.     mp->s = s;
  197.  
  198.     (void) usprintf(s,Banner,Hostname);
  199.  
  200. loop:    if ((cnt = recvline(s,buf,sizeof(buf))) == -1){
  201.         /* He closed on us */
  202.         goto quit;
  203.     }
  204.     if(cnt < 4){
  205.         /* Can't be a legal command */
  206.         usprintf(mp->s,Badcmd);
  207.         goto loop;
  208.     }    
  209.     rip(buf);
  210.     cmd = buf;
  211.  
  212.     /* Translate entire buffer to lower case */
  213.     for(cp = cmd;*cp != '\0';cp++)
  214.         *cp = tolower(*cp);
  215.  
  216.     /* Find command in table; if not present, return syntax error */
  217.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  218.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  219.             break;
  220.     if(*cmdp == NULLCHAR){
  221.         (void) usprintf(mp->s,Badcmd);
  222.         goto loop;
  223.     }
  224.     arg = &cmd[strlen(*cmdp)];
  225.     /* Skip spaces after command */
  226.     while(*arg == ' ')
  227.         arg++;
  228.     /* Execute specific command */
  229.     switch(cmdp-commands){
  230. #ifdef    LZW
  231.     case XLZW_CMD:
  232.         if(!Smtpslzw) {
  233.             usprintf(mp->s,Badcmd);
  234.         } else {
  235.             lzwmode = lzwbits = 0;
  236.             sscanf(arg,"%d %d",&lzwbits,&lzwmode);
  237.             if(!((lzwmode == 0 || lzwmode == 1)
  238.               && (lzwbits > 8 && lzwbits < 17))) {
  239.                 lzwmode = LZWCOMPACT;
  240.                 lzwbits = LZWBITS;
  241.                 usprintf(mp->s,Badcmd);
  242.             } else {
  243.                 usprintf(mp->s,LZWOk,lzwbits,lzwmode);
  244.                 lzwinit(mp->s,lzwbits,lzwmode);
  245.             }
  246.         }
  247.         break;
  248. #endif
  249.     case HELO_CMD:
  250.         free(mp->system);
  251.         mp->system = strdup(arg);
  252.         (void) usprintf(mp->s,Ourname,Hostname);
  253.         break;
  254.     case NOOP_CMD:
  255.         (void) usprintf(mp->s,Ok);
  256.         break;
  257.     case MAIL_CMD:
  258.         if((cp = getname(arg)) == NULLCHAR){
  259.             (void) usprintf(mp->s,Syntax);
  260.             break;
  261.         }
  262.         free(mp->from);
  263.         mp->from = strdup(cp);
  264.         (void) usprintf(mp->s,Ok);
  265.         break;
  266.     case QUIT_CMD:
  267.         (void) usprintf(mp->s,Closing);
  268.         goto quit;
  269.     case RCPT_CMD:    /* Specify recipient */
  270.         if((cp = getname(arg)) == NULLCHAR){
  271.             (void) usprintf(mp->s,Syntax);
  272.             break;
  273.         }
  274.         /* rewrite address if possible */
  275.         if((newaddr = rewrite_address(cp)) != NULLCHAR){
  276.             strcpy(buf,newaddr);
  277.             cp = buf;
  278.             free(newaddr);
  279.         }
  280.  
  281.         /* check if address is ok */
  282.         if ((address_type = validate_address(cp)) == BADADDR){
  283.             (void) usprintf(mp->s,Unknown,cp);
  284.             break;
  285.         }
  286.         /* if a local address check for an alias */
  287.         if (address_type == LOCAL)
  288.             expandalias(&mp->to, cp);
  289.         else
  290.             /* a remote address is added to the list */
  291.             addlist(&mp->to, cp, address_type);
  292.  
  293.         (void) usprintf(mp->s,Ok);
  294.         break;
  295.     case HELP_CMD:
  296.         (void) usprintf(mp->s,Help);
  297.         break;
  298.     case DATA_CMD:
  299.         if(mp->to == NULLLIST)
  300.             (void) usprintf(mp->s,Needrcpt);
  301.         else if ((mp->data = tmpfile()) == NULLFILE)
  302.             (void) usprintf(mp->s,Ioerr);
  303.          else
  304.             getmsgtxt(mp);
  305.         break;
  306.     case RSET_CMD:
  307.         del_list(mp->to);
  308.         mp->to = NULLLIST;
  309.         (void) usprintf(mp->s,Reset);
  310.         break;
  311.     case EXPN_CMD:
  312.         if (*arg == '\0'){
  313.             (void) usprintf(mp->s,Syntax);
  314.             break;
  315.         }
  316.  
  317.         list = NULLLIST;
  318.         /* rewrite address if possible */
  319.         if((newaddr = rewrite_address(arg)) != NULLCHAR)
  320.             if(strcmp(newaddr,arg) == 0){
  321.                 free(newaddr);
  322.                 newaddr = NULLCHAR;
  323.             } else {
  324.                 strcpy(buf,newaddr);
  325.                 arg = buf;
  326.             }
  327.         list = NULLLIST;
  328.         expandalias(&list,arg);
  329.         if (strcmp(list->val,arg) == 0 && list->next == NULLLIST)
  330.             if(newaddr == NULLCHAR){
  331.                 (void) usprintf(mp->s,Noalias,arg);
  332.                 del_list(list);
  333.                 break;
  334.             }
  335.         ap = list;
  336.         while (ap->next != NULLLIST){
  337.             (void) usprintf(mp->s,"250-%s\n",ap->val);
  338.             ap = ap->next;
  339.         }
  340.         usprintf(mp->s,"250 %s\n",ap->val);
  341.         del_list(list);
  342.         free(newaddr);
  343.         break;
  344.     }
  345.     goto loop;
  346.  
  347. quit:
  348.     log(mp->s,"close SMTP");
  349.     close_s(mp->s);
  350.     mail_clean(mp);
  351.     smtptick(NULL);            /* start SMTP daemon immediately */
  352. }
  353.  
  354. extern char shortversion[];
  355. extern char *Mbfwdinfo;
  356.  
  357. /* read the message text
  358.  * This now checks for Message-Id's that could be bids
  359.  * and deletes the message if so. - WG7J
  360.  */
  361. static int
  362. getmsgtxt(mp)
  363. struct smtpsv *mp;
  364. {
  365.     char buf[LINELEN];
  366.     register char *p = buf;
  367.     long t;
  368.     char *cp;
  369. #ifdef MBFWD
  370.     int idnotfound;
  371.     time_t now;
  372.     char bid[LINELEN];
  373.     FILE *fp;
  374. #endif
  375.  
  376.     /* Add timestamp; ptime adds newline */
  377.     time(&t);
  378.     fprintf(mp->data,Hdrs[RECEIVED]);
  379.     if(mp->system != NULLCHAR)
  380.         fprintf(mp->data,"from %s ",mp->system);
  381. #ifdef MBFWD
  382.     fprintf(mp->data,"by %s (%s) with SMTP\n\tid AA%ld ; %s",
  383.             Hostname, (Mbfwdinfo != NULLCHAR) ? Mbfwdinfo : shortversion, \
  384.             get_msgid(), ptime(&t));
  385. #else
  386.     fprintf(mp->data,"by %s (%s) with SMTP\n\tid AA%ld ; %s",
  387.             Hostname, shortversion, get_msgid(), ptime(&t));
  388. #endif
  389.     if(ferror(mp->data)){
  390.         (void) usprintf(mp->s,Ioerr);
  391.         return 1;
  392.     } else {
  393.         (void) usprintf(mp->s,Enter);
  394.     }
  395. #ifdef MBFWD
  396.     idnotfound = 1;
  397. #endif
  398.     while(1){
  399.         if(recvline(mp->s,p,sizeof(buf)) == -1){
  400.             return 1;
  401.         }
  402.         rip(p);
  403.         /* check for end of message ie a . or escaped .. */
  404.         if (*p == '.'){
  405.             if (*++p == '\0'){
  406. #ifdef MBFWD
  407.                 /* Also sends appropriate response */
  408.                 /* if duplicate BID, do not send - WG7J */
  409.                 if(mp->dupbid)
  410.                     (void) usprintf(mp->s,DupBidMsg);
  411.                 else
  412. #endif
  413.                 if(mailit(mp->data,mp->from,mp->to) != 0)
  414.                     (void) usprintf(mp->s,Ioerr);
  415.                 else
  416.                     (void) usprintf(mp->s,Sent);
  417.                 fclose(mp->data);
  418.                 mp->data = NULLFILE;
  419.                 del_list(mp->to);
  420.                 mp->to = NULLLIST;
  421. #ifdef MBFWD
  422.                 /* If there is a BID set, save it in the history file - WG7J */
  423.                 if(mp->bid != NULLCHAR)
  424.                     if((fp = fopen(Historyfile,APPEND_TEXT)) != NULL) {
  425.                         /* Timestamp added to allow automatic expiry
  426.                          * of bid file - WG7J
  427.                          */
  428.                         time(&now);
  429.                         fprintf(fp,"%s %ld\n",mp->bid,now); /* Save BID */
  430.                         fclose(fp);
  431.             /* jpd@UCS.usl.edu: memory leak */
  432.             free(mp->bid);
  433.             mp->bid = NULLCHAR;
  434.                     }
  435. #endif
  436.                 return 0;
  437.             } else if (!(*p == '.' && *(p+1) == '\0'))
  438.                 p--;
  439.         }
  440.         /* for UNIX mail compatiblity */
  441.         if (strncmp(p,"From ",5) == 0)
  442.             (void) putc('>',mp->data);
  443.         /* Append to data file */
  444. #ifdef MSDOS
  445.     /* change suggested by "Erik Olson" <erik@marge.phys.washington.edu> */
  446.     while ((cp = strchr(p,CTLZ))!=NULL) /* Change control-Z's to \n */
  447.             *cp='\n';
  448. #endif
  449.         if(fprintf(mp->data,"%s\n",p) < 0){
  450.             (void) usprintf(mp->s,Ioerr);
  451.             return 1;
  452.         }
  453. #ifdef MBFWD
  454.         /* Check for Message-Id string - WG7J */
  455.         if(idnotfound && !strnicmp(p,Hdrs[MSGID],11)) {
  456.             if((cp = getname(p)) == NULLCHAR)
  457.                 continue;
  458.             idnotfound = 0;
  459.             strcpy(bid,cp);
  460.             if((cp = strchr(bid,'@')) == NULLCHAR)
  461.                 continue;
  462.             /* A trailing ".bbs" indicates that the Message-ID was generated
  463.              * from a BBS style message, and not a RFC-822 message.
  464.              */
  465.             if(stricmp(&bid[strlen(bid) - 4], ".bbs") == 0) {
  466.                 *cp = '\0'; /*retain the bid given by user*/
  467.                 bid[13] = '\0'; /* BIDs should be no longer than 13 bytes */
  468.                 /* now check it, and save if not duplicate - WG7J */
  469.                 if((mp->dupbid = msgidcheck(bid)) == 0)
  470.                     mp->bid = strdup(bid);
  471.             }
  472.         }
  473. #endif
  474.     }
  475. }
  476.  
  477. /* Create control block, initialize */
  478. static struct smtpsv *
  479. mail_create()
  480. {
  481.     register struct smtpsv *mp;
  482.  
  483.     mp = (struct smtpsv *)callocw(1,sizeof(struct smtpsv));
  484.     mp->from = strdup("");    /* Default to null From address */
  485.     return mp;
  486. }
  487.  
  488. /* Free resources, delete control block */
  489. static void
  490. mail_clean(mp)
  491. register struct smtpsv *mp;
  492. {
  493.     if (mp == NULLSMTPSV)
  494.         return;
  495.     free(mp->system);
  496.     free(mp->from);
  497.     free(mp->bid);
  498.     if(mp->data != NULLFILE)
  499.         fclose(mp->data);
  500.     del_list(mp->to);
  501.     free((char *)mp);
  502. }
  503.  
  504. /* General mailit function. It takes a list of addresses which have already
  505. ** been verified and expanded for aliases. Base on the current mode the message
  506. ** is place in an mbox, the outbound smtp queue or the rqueue interface
  507. */
  508. /* Modified to touch the timestamp file for new-message tracking on local
  509.  * deliveries... - WG7J.
  510.  */
  511. static int
  512. mailit(data,from,tolist)
  513. FILE *data;
  514. char *from;
  515. struct list *tolist;
  516. {
  517.     struct list *ap, *dlist = NULLLIST;
  518.     register FILE *fp;
  519.     char    mailbox[50], *cp, *host, *qhost;
  520.     int    c, fail = 0;
  521.     time_t    t;
  522.     extern int Smtpquiet;
  523.     int index;
  524.  
  525.     if ((Smtpmode & QUEUE) != 0)
  526.         return(router_queue(data,from,tolist));
  527.  
  528.     do {
  529.         qhost = NULLCHAR;
  530.         for(ap = tolist;ap != NULLLIST;ap = ap->next)
  531.             if (ap->type == DOMAIN){
  532.                 if ((host = strrchr(ap->val,'@')) != NULLCHAR)
  533.                     host++;
  534.                 else
  535.                     host = Hostname;
  536.                 if(qhost == NULLCHAR)
  537.                          qhost = host;
  538.                 if(stricmp(qhost,host) == 0){
  539.                     ap->type = BADADDR;
  540.                     addlist(&dlist,ap->val,0);
  541.                 }
  542.             }
  543.         if(qhost != NULLCHAR){
  544.             rewind(data);
  545.             queuejob(data,qhost,dlist,from);
  546.             del_list(dlist);
  547.             dlist = NULLLIST;
  548.         }
  549.     } while(qhost != NULLCHAR);
  550.  
  551. #ifdef    NNTPS
  552.     for(ap = tolist;ap != NULLLIST;ap = ap->next){
  553.         if(ap->type != NNTP_GATE)
  554.             continue;
  555.         nnGpost(data,from,ap);
  556.         ap->type = BADADDR;
  557.     }
  558. #endif
  559.  
  560.     index = 0;
  561.     for(ap = tolist;ap != NULLLIST;ap = ap->next,index++){
  562.         if(ap->type != LOCAL){
  563.             ap->type = DOMAIN;
  564.             continue;
  565.         }
  566.         rewind(data);
  567.         /* strip off host name of LOCAL addresses */
  568.         if ((cp = strchr(ap->val,'@')) != NULLCHAR)
  569.             *cp = '\0';
  570.  
  571.         /* replace '\' and '.' with '/',
  572.          * this allows mailing into subdirs of spool/mail - WG7J
  573.          */
  574.         cp = ap->val;
  575.         while((cp=strchr(cp,'.')) != NULLCHAR)
  576.             *cp++ = '/';
  577.         cp = ap->val;
  578.         while((cp=strchr(cp,'\\')) != NULLCHAR)
  579.             *cp++ = '/';
  580.  
  581.         /* truncate long user names */
  582. /*        if (strlen(ap->val) > MBOXLEN)
  583.              ap->val[MBOXLEN] = '\0';
  584.  */
  585.  
  586.         /* if mail file is busy save it in our smtp queue
  587.          * and let the smtp daemon try later.
  588.          */
  589.         if (mlock(Mailspool,ap->val)){
  590.             addlist(&dlist,ap->val,0);
  591.             fail = queuejob(data,Hostname,dlist,from);
  592.             del_list(dlist);
  593.             dlist = NULLLIST;
  594.         } else {
  595.             char buf[LINELEN];
  596.             int tocnt = 0;
  597.             sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
  598. #ifndef    AMIGA
  599.             if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE){
  600. #else
  601.             if((fp = fopen(mailbox,"r+")) != NULLFILE){
  602.                 (void) fseek(fp, 0L, 2);
  603. #endif
  604.                 time(&t);
  605.                 fprintf(fp,"From %s %s",from,ctime(&t));
  606.                 host = NULLCHAR;
  607.                 while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  608.                     if(buf[0] == '\n'){
  609.                         if(tocnt == 0)
  610.                             fprintf(fp,"%s%s\n",
  611.                                 Hdrs[APPARTO],
  612.                                 ap->val);
  613.                         fputc('\n',fp);
  614.                         break;
  615.                     }
  616.                     /* get a unique, new id for all messages - from KO4KS */
  617.                     if(index & htype(buf) == MSGID) {
  618.                         /*KD4CIM*/
  619.                         if(strstr(buf,Hostname) == NULL)    /* Was it a BID? */
  620.                             fputs(buf,fp);      /* yep, best keep that one */
  621.                         else  /* No, give it a new one */
  622.                             fprintf(fp,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
  623.                     } else
  624.                         fputs(buf,fp);
  625.                     rip(buf);
  626.                     switch(htype(buf)){
  627.                     case TO:
  628.                     case CC:
  629.                         ++tocnt;
  630.                         break;
  631.                     case RRECEIPT:
  632.                         if((cp = getaddress(buf,0))
  633.                            != NULLCHAR){
  634.                             free(host);
  635.                             host = strdup(cp);
  636.                         }
  637.                         break;
  638.                     }
  639.                 }
  640.                 while((c = fread(buf,1,sizeof(buf),data)) > 0)
  641.                     if(fwrite(buf,1,(size_t)c,fp) != c)
  642.                         break;
  643.                 if(ferror(fp))
  644.                     fail = 1;
  645.                 else
  646.                     fprintf(fp,"\n");
  647.                 /* Leave a blank line between msgs */
  648.                 fclose(fp);
  649. /* If we use tprintf here, instead of printf, flowcontrol
  650.  * in the command screen is used; if the system is unattended for
  651.  * more then 24 messages coming in, it will lock up mail delivery.
  652.  * Make sure this only goes to the command screen - WG7J
  653.  */
  654. #ifdef UNIX
  655. /* true, but we defeat that when using the trace interface anyway.  KF8NH */
  656.         tcmdprintf("New mail for %s from <%s>%c\n",ap->val,from,(Smtpquiet? ' ': '\007'));
  657. #else
  658.                 if(Current->output == Command->output)
  659.                     printf("New mail for %s from <%s>%c\n",ap->val,from, Smtpquiet ? ' ' : '\007');
  660. #endif
  661.                 if(host != NULLCHAR){
  662.                     rewind(data); /* Send return receipt */
  663.                     mdaemon(data,host,NULLLIST,0);
  664.                     free(host);
  665.                 }
  666.             } else 
  667.                 fail = 1;
  668.             (void) rmlock(Mailspool,ap->val);
  669.             if (fail)
  670.                 break;
  671.  
  672. #ifdef USERLOG
  673.             /* Now touch the timestamp file if it's an area - WG7J */
  674.             if(isarea(ap->val)) {
  675.                 sprintf(mailbox,"%s/%s.inf",Mailspool,ap->val);
  676.                 fclose(fopen(mailbox,"w"));
  677.             }
  678. #endif
  679.             /* make a log entry */
  680.             smtplog("deliver: To: %s From: %s",ap->val,from);
  681.         }
  682.     }
  683.     return fail;
  684. }
  685.  
  686. /* Return Date/Time in Arpanet format in passed string */
  687. char *
  688. ptime(t)
  689. long *t;
  690. {
  691.     /* Print out the time and date field as
  692.      *        "DAY day MONTH year hh:mm:ss ZONE"
  693.      */
  694.     register struct tm *ltm;
  695.     static char tz[4];
  696.     static char str[40];
  697.     char *p;
  698.     /* Read the system time */
  699.     ltm = localtime(t);
  700.  
  701.     if (*tz == '\0')
  702.         if ((p = getenv("TZ")) == NULL)
  703.             strcpy(tz,"UTC");
  704.         else
  705.             strncpy(tz,p,3);
  706.  
  707.     /* rfc 822 format */
  708.     sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n",
  709.         Days[ltm->tm_wday],
  710.         ltm->tm_mday,
  711.         Months[ltm->tm_mon],
  712.         ltm->tm_year,
  713.         ltm->tm_hour,
  714.         ltm->tm_min,
  715.         ltm->tm_sec,
  716.         tz);
  717.     return(str);
  718. }
  719.  
  720. long 
  721. get_msgid()
  722. {
  723.     char sfilename[LINELEN];
  724.     char s[20];
  725.     register long sequence = 0;
  726.     FILE *sfile;
  727.  
  728. #ifdef UNIX
  729.     int lfd, cnt;
  730.     long pid;
  731.  
  732.     /*
  733.      * I have a filter (u2j) which injects messages into JNOS for SMTP
  734.      * delivery.  It's a good idea to make sure the sequence file is locked
  735.      * while we update it, so JNOS and u2j don't get into a race for the
  736.      * next message ID.
  737.      */
  738.     sprintf(sfilename, "%s/sequence.lck", Mailqdir);
  739.     cnt = 0;
  740.     while ((lfd = open(sfilename, O_WRONLY|O_CREAT|O_EXCL, 0600)) == -1)
  741.     {
  742.         if (errno != EEXIST || ++cnt == 5)
  743.         {
  744.         log(-1, "can't lock sequence file %s: %s", sfilename,
  745.             strerror(errno));
  746.         where_outta_here(1);
  747.         }
  748.         if ((lfd = open(sfilename, O_RDONLY)) != -1)
  749.         {
  750.         sfile = fdopen(lfd, "r");
  751.         fscanf(sfile, "%ld", &pid);
  752.         fclose(sfile);
  753.         if (kill(pid, 0) == -1 && errno == ESRCH)
  754.         {
  755.             unlink(sfilename);
  756.             continue;
  757.         }
  758.         }
  759.         pause(500);
  760.     }
  761.     sprintf(sfilename, "%10d\n", getpid());
  762.     write(lfd, sfilename, strlen(sfilename));
  763.     close(lfd);
  764. #endif
  765.     sprintf(sfilename,"%s/sequence.seq",Mailqdir);
  766.     sfile = fopen(sfilename,READ_TEXT);
  767.  
  768.     /* if sequence file exists, get the value, otherwise set it */
  769.     if (sfile != NULL){
  770.         (void) fgets(s,sizeof(s),sfile);
  771.         sequence = atol(s);
  772.         /* Keep it in range of a 5 digit number to use for dos name prefix. */
  773. #ifdef UNIX
  774.     if (sequence < 1L || sequence > 999999999L)
  775. #else
  776.         if (sequence < 1L || sequence > 99999L )
  777. #endif
  778.             sequence = 1;
  779.         fclose(sfile);
  780.     }
  781.  
  782.     /* increment sequence number, and write to sequence file */
  783.     sfile = fopen(sfilename,WRITE_TEXT);
  784.     fprintf(sfile,"%ld",++sequence);
  785.     fclose(sfile);
  786. #ifdef UNIX
  787.     sprintf(sfilename, "%s/sequence.lck", Mailqdir);
  788.     unlink(sfilename);
  789. #endif
  790.     return sequence;
  791. }
  792.  
  793. /* test if mail address is valid */
  794. int
  795. validate_address(s)
  796. char *s;
  797. {
  798.     char *cp;
  799.     int32 addr;
  800.  
  801.     if(*s == '!'){
  802. #ifdef    NNTPS
  803.         if((cp = strpbrk(s,"%@.,/")) != NULLCHAR)
  804.             *cp = '\0';
  805.         return NNTP_GATE;
  806. #else
  807.         return BADADDR;
  808. #endif
  809.     }
  810.     /* if address has @ in it then check dest address */
  811.     if ((cp = strrchr(s,'@')) != NULLCHAR){
  812.         cp++;
  813.         /* 1st check if it is our hostname.
  814.         * if not then check the hosts file and see if we can
  815.         * resolve the address to a know site or one of our aliases.
  816.         */
  817.         if(stricmp(cp,Hostname) != 0){
  818.             if ((addr = mailroute(cp)) == 0
  819.                 && (Smtpmode & QUEUE) == 0)
  820.                 return BADADDR;
  821.             if (ismyaddr(addr) == NULLIF)
  822.                 return DOMAIN;
  823.         }
  824.         
  825.         /* on a local address remove the host name part */
  826.         *--cp = '\0';
  827.     }
  828.  
  829.     /* if using an external router leave address alone */
  830.     if ((Smtpmode & QUEUE) != 0)
  831.         return LOCAL;
  832.  
  833.     /* check for the user%host hack */
  834.     if ((cp = strrchr(s,'%')) != NULLCHAR){
  835.         *cp = '@';
  836.         cp++;
  837.         /* reroute based on host name following the % seperator */
  838.         if (mailroute(cp) == 0)
  839.             return BADADDR;
  840.         else
  841.             return DOMAIN;
  842.     }
  843.  
  844. #ifdef MSDOS    /* dos file name checks */
  845.     /* Check for characters illegal in MS-DOS file names */
  846.     for(cp = s;*cp != '\0';cp++){
  847.         /* Accept '.', '/', and '\' !
  848.          * that way we can mail into subdirs - WG7J
  849.          */
  850.         if(*cp == '.' || *cp == '\\' || *cp == '/')
  851.             continue;
  852.         if(dosfnchr(*cp) == 0){
  853.             return BADADDR;    
  854.         }
  855.     }
  856. #endif
  857.     return LOCAL;
  858. }
  859.  
  860. /* place a mail job in the outbound queue */
  861. int
  862. queuejob(dfile,host,to,from)
  863. FILE *dfile;
  864. char *host;
  865. struct list *to;
  866. char *from;
  867. {
  868.     FILE *fp;
  869.     struct list *ap;
  870.     char tmpstring[50], prefix[9], buf[LINELEN];
  871.     register int cnt;
  872.  
  873.     sprintf(prefix,"%ld",get_msgid());
  874.     mlock(Mailqdir,prefix);
  875.     sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix);
  876.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  877.         (void) rmlock(Mailqdir,prefix);
  878.         return 1;
  879.     }
  880.     while((cnt = fread(buf, 1, LINELEN, dfile)) > 0)
  881.         if(fwrite(buf, 1, (size_t)cnt, fp) != cnt)
  882.             break;
  883.     if(ferror(fp)){
  884.         fclose(fp);
  885.         (void) rmlock(Mailqdir,prefix);
  886.         return 1;
  887.     }
  888.     fclose(fp);
  889.     sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix);
  890.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  891.         (void) rmlock(Mailqdir,prefix);
  892.         return 1;
  893.     }
  894.     fprintf(fp,"%s\n%s\n",host,from);
  895.     for(ap = to; ap != NULLLIST; ap = ap->next){
  896.         fprintf(fp,"%s\n",ap->val);
  897.         smtplog("queue job %s To: %s From: %s",prefix,ap->val,from);
  898.     }
  899.     fclose(fp);
  900.     (void) rmlock(Mailqdir,prefix);
  901.     return 0;
  902. }
  903.  
  904. /* Deliver mail to the appropriate mail boxes */
  905. static int
  906. router_queue(data,from,to)
  907. FILE *data;
  908. char *from;
  909. struct list *to;
  910. {
  911.     int c;
  912.     register struct list *ap;
  913.     FILE *fp;
  914.     char tmpstring[50];
  915.     char prefix[9];
  916.  
  917.     sprintf(prefix,"%ld",get_msgid());
  918.     mlock(Routeqdir,prefix);
  919.     sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix);
  920.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  921.         (void) rmlock(Routeqdir,prefix);
  922.         return 1;
  923.     }
  924.     rewind(data);
  925.     while((c = getc(data)) != EOF)
  926.         if(putc(c,fp) == EOF)
  927.             break;
  928.     if(ferror(fp)){
  929.         fclose(fp);
  930.         (void) rmlock(Routeqdir,prefix);
  931.         return 1;
  932.     }
  933.     fclose(fp);
  934.     sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix);
  935.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  936.         (void) rmlock(Routeqdir,prefix);
  937.         return 1;
  938.     }
  939.     fprintf(fp,"From: %s\n",from);
  940.     for(ap = to;ap != NULLLIST;ap = ap->next){
  941.         fprintf(fp,"To: %s\n",ap->val);
  942.     }
  943.     fclose(fp);
  944.     (void) rmlock(Routeqdir,prefix);
  945.     smtplog("rqueue job %s From: %s",prefix,from);
  946.     return 0;
  947. }
  948.  
  949. /* add an element to the front of the list pointed to by head 
  950. ** return NULLLIST if out of memory.
  951. */
  952. struct list *
  953. addlist(head,val,type)
  954. struct list **head;
  955. char *val;
  956. int type;
  957. {
  958.     register struct list *tp;
  959.  
  960.     tp = (struct list *)callocw(1,sizeof(struct list));
  961.  
  962.     tp->next = NULLLIST;
  963.  
  964.     /* allocate storage for the char string */
  965.     tp->val = strdup(val);
  966.     tp->type = type;
  967.  
  968.     /* add entry to front of existing list */
  969.     if (*head == NULLLIST)
  970.         *head = tp;
  971.     else {
  972.         tp->next = *head;
  973.         *head = tp;
  974.     }
  975.     return tp;
  976.  
  977. }
  978.  
  979. #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
  980. #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
  981.  
  982. /* check for and alias and expand alias into a address list */
  983. static struct list *
  984. expandalias(head, user)
  985. struct list **head;
  986. char *user;
  987. {
  988.     FILE *fp;
  989.     register char *s,*p;
  990.     struct rr *rrp, *rrlp;
  991.     int inalias = 0;
  992.     struct list *tp;
  993.     char buf[LINELEN];
  994.     
  995. #ifdef __GNUC__
  996.     tp = 0;            /* silence "uninitialized" warning */
  997. #endif
  998.     /* no alias file found */
  999.     if ((fp = fopen(Alias, READ_TEXT)) == NULLFILE){
  1000.         /* Try MB, MG or MR domain name records */
  1001.         rrlp = rrp = resolve_mailb(user);
  1002.         while(rrp != NULLRR){
  1003.             if(rrp->rdlength > 0){
  1004.                 /* remove the trailing dot */
  1005.                 rrp->rdata.name[rrp->rdlength-1] = '\0';
  1006.                 /* replace first dot with @ if there is no @ */
  1007.                 if(strchr(rrp->rdata.name,'@') == NULLCHAR
  1008.                    && (p = strchr(rrp->rdata.name,'.')) !=
  1009.                    NULLCHAR)
  1010.                     *p = '@';
  1011.                 if(strchr(rrp->rdata.name,'@') != NULLCHAR)
  1012.                     tp = addlist(head,rrp->rdata.name,
  1013.                              DOMAIN);
  1014.                 else
  1015.                     tp = addlist(head,rrp->rdata.name,
  1016.                              LOCAL);
  1017.                 ++inalias;
  1018.             }
  1019.             rrp = rrp->next;
  1020.         }
  1021.         free_rr(rrlp);
  1022.         if(inalias)
  1023.             return tp;
  1024.         else
  1025.             return addlist(head, user, LOCAL);
  1026.     }
  1027.  
  1028.     while (fgets(buf,LINELEN,fp) != NULLCHAR){
  1029.         p = buf;
  1030.         if ( *p == '#' || *p == '\0')
  1031.             continue;
  1032.         rip(p);
  1033.  
  1034.         /* if not in an matching entry skip continuation lines */
  1035.         if (!inalias && isspace(*p))
  1036.             continue;
  1037.  
  1038.         /* when processing an active alias check for a continuation */
  1039.         if (inalias){
  1040.             if (!isspace(*p)) 
  1041.                 break;    /* done */
  1042.         } else {
  1043.             s = p;
  1044.             SKIPWORD(p);
  1045.             *p++ = '\0';    /* end the alias name */
  1046.             if (strcmp(s,user) != 0)
  1047.                 continue;    /* no match go on */
  1048.             inalias = 1;
  1049.         }
  1050.  
  1051.         /* process the recipients on the alias line */
  1052.         SKIPSPACE(p);
  1053.         while(*p != '\0' && *p != '#'){
  1054.             s = p;
  1055.             SKIPWORD(p);
  1056.             if (*p != '\0')
  1057.                 *p++ = '\0';
  1058.  
  1059.             /* find hostname */
  1060. #ifdef    NNTPS
  1061.             if(*s == '!')
  1062.                 tp = addlist(head,s,NNTP_GATE);
  1063.             else
  1064. #endif
  1065.             if (strchr(s,'@') != NULLCHAR)
  1066.                 tp = addlist(head,s,DOMAIN);
  1067.             else
  1068.                 tp = addlist(head,s,LOCAL);
  1069.             SKIPSPACE(p);
  1070.         }
  1071.     }
  1072.     (void) fclose(fp);
  1073.  
  1074.     if (inalias)    /* found and processed and alias. */
  1075.         return tp;
  1076.  
  1077.     /* no alias found treat as a local address */
  1078.     return addlist(head, user, LOCAL);
  1079. }
  1080.  
  1081. #if    defined(ANSIPROTO)
  1082. static void
  1083. smtplog(char *fmt, ...)
  1084. {
  1085.     va_list ap;
  1086.     char *cp;
  1087.     long t;
  1088.     FILE *fp;
  1089.  
  1090.     if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  1091.         return;
  1092.     time(&t);
  1093.     cp = ctime(&t);
  1094.     rip(cp);
  1095.     fprintf(fp,"%s ",cp);
  1096.     va_start(ap,fmt);
  1097.     vfprintf(fp,fmt,ap);
  1098.     va_end(ap);
  1099.     fprintf(fp,"\n");
  1100.     fclose(fp);
  1101. }
  1102.  
  1103. #else
  1104.  
  1105. static void
  1106. smtplog(fmt,arg1,arg2,arg3,arg4)
  1107. char *fmt;
  1108. int arg1,arg2,arg3,arg4;
  1109. {
  1110.     char *cp;
  1111.     long t;
  1112.     FILE *fp;
  1113.  
  1114.     if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  1115.         return;
  1116.     time(&t);
  1117.     cp = ctime(&t);
  1118.     rip(cp);
  1119.     fprintf(fp,"%s ",cp);
  1120.     fprintf(fp,fmt,arg1,arg2,arg3,arg4);
  1121.     fprintf(fp,"\n");
  1122.     fclose(fp);
  1123. }
  1124. #endif
  1125.  
  1126. /* send mail to a single user. Can be called from the ax25 mailbox or
  1127. ** from the return mail function in the smtp client 
  1128. */
  1129. static int
  1130. mailuser(data,from,to)
  1131. FILE *data;
  1132. char *from;
  1133. char *to;
  1134. {
  1135.  
  1136.         int address_type, ret;
  1137.         struct list *tolist = NULLLIST;
  1138.  
  1139.         /* check if address is ok */
  1140.         if ((address_type = validate_address(to)) == BADADDR){
  1141.             return 1;
  1142.         }
  1143.         /* if a local address check for an alias */
  1144.         if (address_type == LOCAL)
  1145.             expandalias(&tolist, to);
  1146.         else
  1147.             /* a remote address is added to the list */
  1148.             addlist(&tolist, to, address_type);
  1149.         ret = mailit(data,from,tolist);
  1150.         del_list(tolist);
  1151.         return ret;
  1152.  
  1153. }
  1154.  
  1155. /* Mailer daemon return mail mechanism */
  1156. int
  1157. mdaemon(data,to,lp,bounce)
  1158. FILE *data;        /* pointer to rewound data file */
  1159. char *to;        /* Overridden by Errors-To: line if bounce is true */
  1160. struct list *lp;    /* error log for failed mail */
  1161. int bounce;        /* True for failed mail, otherwise return receipt */
  1162. {
  1163.     time_t t;
  1164.     FILE *tfile;
  1165.     char buf[LINELEN], *cp, *newto = NULLCHAR;
  1166.     int cnt;
  1167.     if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce){
  1168.         while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  1169.             if(buf[0] == '\n')
  1170.                 break;
  1171.             /* Look for Errors-To: */
  1172.             if(htype(buf) == ERRORSTO &&
  1173.                (cp = getaddress(buf,0)) != NULLCHAR){
  1174.                 free(newto);
  1175.                 newto = strdup(cp);
  1176.                 break;
  1177.             }
  1178.         }
  1179.         if(newto == NULLCHAR && ((to != NULLCHAR && *to == '\0') ||
  1180.            to == NULLCHAR))
  1181.             return -1;
  1182.         rewind(data);
  1183.     }
  1184.     if((tfile = tmpfile()) == NULLFILE)
  1185.         return -1;
  1186.     time(&t);
  1187.     fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&t));
  1188.     fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
  1189.     fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n",
  1190.         Hdrs[FROM],Hostname);
  1191.     fprintf(tfile,"%s%s\n",Hdrs[TO],newto != NULLCHAR ? newto : to);
  1192.     fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT],
  1193.         bounce ? "Failed mail" : "Return receipt");
  1194.     if(bounce){
  1195.         fprintf(tfile,"  ===== transcript follows =====\n\n");
  1196.         for (; lp != NULLLIST; lp = lp->next)
  1197.             fprintf(tfile,"%s\n",lp->val);
  1198.         fprintf(tfile,"\n");
  1199.     }
  1200.     fprintf(tfile,"  ===== %s follows ====\n",
  1201.         bounce ? "Unsent message" : "Message header");
  1202.  
  1203.     while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  1204.         if(buf[0] == '\n')
  1205.             break;
  1206.         fputs(buf,tfile);
  1207.     }
  1208.     if(bounce){
  1209.         fputc('\n',tfile);
  1210.         while((cnt = fread(buf,1,sizeof(buf),data)) > 0)
  1211.             fwrite(buf,1,(size_t)cnt,tfile);
  1212.     }
  1213.     fseek(tfile,0L,0);
  1214.     /* A null From<> so no looping replys to MAIL-DAEMONS */
  1215.     (void) mailuser(tfile,"",newto != NULLCHAR ? newto : to);
  1216.     fclose(tfile);
  1217.     free(newto);
  1218.     return 0;
  1219. }
  1220.  
  1221. #ifdef MSDOS
  1222. static int
  1223. dosfnchr(ch)
  1224. int ch;
  1225. {
  1226.     int i, j;
  1227.  
  1228.     i = (ch & 0xf8) >> 3;
  1229.     j = doschars[i] & (1 << (ch & 0x07));
  1230.     return j;
  1231. }
  1232. #endif
  1233.